-- Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
-- 
-- this file licensed under the Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
--
-- THIS SOFTWARE IS PROVIDED ON AN \"AS IS\" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
-- PURPOSE.
-- See the Mulan PSL v2 for more details.

local class = require 'mc.class'
local class_mgnt = require 'mc.class_mgnt'
local Singleton = require 'mc.singleton'
local signal = require 'mc.signal'
local log = require 'mc.logging'
local custom_msg = require 'messages.custom'
local privilege = require 'mc.privilege'
local utils = require 'basic_cooling.cooling_utils'
local f_enum = require 'class.types.types'
local cooling_enums = require 'basic_cooling.define.cooling_enums'
local props = require 'basic_cooling.define.cooling_properties'
local enums = require 'basic_cooling.define.cooling_enums'
local cooling_policys = require 'basic_cooling.cooling_policys'
local mc_utils = require 'mc.utils'
local sqlite3 = require 'lsqlite3'
local cjson = require 'cjson'
local fans_config = require 'basic_cooling.cooling_config.fans_config'

local SMART_COOLING_TABLE_LENGTH<const> = 3
local cooling_requirements = class()
local MAX_REQ_ID<const> = 0xff
local START_SLOT<const> = 255

local POLICY_TYPE_MAP<const> = {
    [1] = 'Inlet',
    [2] = 'Disk'
}
local TEMPERATURE_TYPE_MAP<const> = {
    [1] = 'Cpu',
    [2] = 'Outlet',
    [3] = 'Disk',
    [4] = 'Memory',
    [5] = 'PCH',
    [6] = 'VRD',
    [7] = 'VDDQ',
    [8] = 'NPUHbm',
    [9] = 'NPUAiCore',
    [10] = 'NPUBoard',
    [11] = 'Inlet',
    [12] = 'SoCBoardOutlet',
    [13] = 'SoCBoardInlet',
    [0xffffffff] = 'Unknown',
}

function cooling_requirements:ctor(db, base_service, cooling_data, persist)
    self.db = db
    self.persist = persist
    self.objs = {}
    self.requirement_id_map = {} -- CoolingRequirement对象的id表，用于冲突判断
    self.is_requirement_updated = false
    self.type_obj_map = {}
    self.requirement_info_changed = signal.new()
    self.base_service = base_service
    self.cooling_areas = cooling_data.cooling_areas -- coolingarea对象表，key为AreaId,value为对象
    self.cooling_data = cooling_data
    self.policys_instance = cooling_policys.get_instance()
    self.fans_config_instance = fans_config.get_instance()
end

function cooling_requirements:init()
    -- 私有属性属性变更监听
    class_mgnt('CoolingRequirement').property_changed:on(function(object, prop, value)
        self:private_property_changed_callback(object, prop, value)
    end)
end

-- 获取Requirement前8位的base_id
local function get_base_id(id)
    if id < MAX_REQ_ID then
        return id
    end
    return (id >> 8) & MAX_REQ_ID
end

-- 获取Requirement后8位的槽位号
local function get_slot(id)
    return id & MAX_REQ_ID
end

local function get_requirement_id(base_id, slot)
    return (base_id << 8) | slot
end

local function get_new_slot(self, base_id)
    local new_requirement_id
    for new_slot = START_SLOT, 1, -1 do
        new_requirement_id = get_requirement_id(base_id, new_slot)
        if not self.requirement_id_map[new_requirement_id] then
            return new_slot
        end
    end
    return 0
end

-- 框架持久化前回调，如果preprocess_db返回true则立刻进行资源上树,否则业务自己上树
function cooling_requirements:on_preprocess_cb(object)
    -- CoolingRequirementId对象主键出现相同值冲突
    if self.requirement_id_map[object[props.REQUIREMENT_ID]] then
        local origin_requirement_id = object[props.REQUIREMENT_ID]
        local base_id = get_base_id(object[props.REQUIREMENT_ID])
        local new_slot = get_new_slot(self, base_id)
        local new_requirement_id = get_requirement_id(base_id, new_slot)
        local ok, err_msg = pcall(object.force_set_prop, object, props.REQUIREMENT_ID, new_requirement_id)
        if not ok then
            log:error('Set the value %s of the conflict property RequirementId of object %s to %s failed, err_msg:%s',
                origin_requirement_id, object[props.OBJECT_NAME], new_requirement_id, err_msg
            )
        else
            self.requirement_id_map[new_requirement_id] = true
            log:warn('Set the value %s of the conflict property RequirementId of object %s to 0x%x successfully',
                origin_requirement_id, object[props.OBJECT_NAME], new_requirement_id
            )
        end
    else
        self.requirement_id_map[object[props.REQUIREMENT_ID]] = true
    end

    object[props.ORIGIN_TARGET_TEMP_CELSIUS] = object[props.TARGET_TEMP_CELSIUS]
    object[props.ORIGIN_FAILED_VALUE] = object[props.FAILED_VALUE]
    object[props.ORIGIN_MAX_ALLOWED_TEMPERATURE_CELSIUS] = object[props.MAX_ALLOWED_TEMPERATURE_CELSIUS]
    if #object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] ~= 0 then
        object[props.ORIGIN_SMART_COOLING_TARTGET_TEMPERATURE] = object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS]
    else
        object[props.ORIGIN_SMART_COOLING_TARTGET_TEMPERATURE] = object[props.SMART_COOLING_TARTGET_TEMPERATURE]
    end
    
    return true
end

function cooling_requirements:obj_add_callback(class_name, object, position)
    object[props.REQ_BASE_ID] = get_base_id(object[props.REQUIREMENT_ID])
    object[props.REQ_SLOT] = get_slot(object[props.REQUIREMENT_ID])
    log:debug('Add CoolingRequirement Id: 0x%x', object[props.REQUIREMENT_ID])
    self.objs[object[props.REQUIREMENT_ID]] = object -- 用CSR配置的Requirement的Id作为key进行存储，应当唯一

    self:set_requirement_updated_flag(true)

    if object[props.TEMPERATURE_TYPE] ~= f_enum.TemperatureType.InvalidType:value() then
        self:insert_to_type_obj_map(object)
    end

    if #object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] ~= 0 then
        object[props.SMART_COOLING_TARTGET_TEMPERATURE] = object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS]
    else
        object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] = object[props.SMART_COOLING_TARTGET_TEMPERATURE]
    end

    object:get_mdb_object('bmc.kepler.Systems.CoolingRequirement')
        .property_before_change:on(function(name, value, sender)
            return self:requirement_props_before_change_callback(object, name, value, sender)
        end)
    object:get_mdb_object('bmc.kepler.Systems.CoolingRequirement')
        .property_changed:on(function(name, value, sender)
            self:requirement_props_changed_callback(object, name, value, sender)
        end)

    self.base_service:ImplCoolingRequirementCoolingRequirementSetTargetTemperature(function(obj, ctx, ...)
        return self:SetTargetTemperature(obj, ctx, ...)
    end)
    self.base_service:ImplCoolingRequirementCoolingRequirementSetSmartCoolingTargetTemperature(function(obj, ctx, ...)
        return self:SetSmartCoolingTargetTemperature(obj, ctx, ...)
    end)
    self.base_service:ImplCoolingRequirementCoolingRequirementSetBasicCoolingTargetTemperature(function(obj, ctx, ...)
        return self:SetBasicCoolingTargetTemperature(obj, ctx, ...)
    end)
    self.base_service:ImplCoolingRequirementCoolingRequirementSetMaxAllowedTemperature(function(obj, ctx, ...)
        return self:SetMaxAllowedTemperature(obj, ctx, ...)
    end)
    self.base_service:ImplCoolingRequirementCoolingRequirementSetFailedValue(function(obj, ctx, ...)
        return self:SetFailedValue(obj, ctx, ...)
    end)
end

function cooling_requirements:requirement_props_before_change_callback(object, name, value, sender)
    return true
end

function cooling_requirements:requirement_props_changed_callback(object, name, value, sender)
    if name == props.TARGET_TEMP_CELSIUS then
        self:set_requirement_updated_flag(true)
    end
end

function cooling_requirements:get_requirement_updated_flag()
    return self.is_requirement_updated
end

function cooling_requirements:set_requirement_updated_flag(flag)
    self.is_requirement_updated = flag
end

function cooling_requirements:get_inlet_requirement()
    if not self.inlet_requirement then
        local inlet_o = self:get_first_obj_by_temp_type(f_enum.TemperatureType.Inlet:value())
        self.inlet_requirement = inlet_o
    end

    return self.inlet_requirement
end

function cooling_requirements:get_cpu_requirements()
    if not self.cpu_requirements then
        local cpu_objs = self:get_objs_by_temp_type(f_enum.TemperatureType.Cpu:value())
        self.cpu_requirements = cpu_objs
    end

    return self.cpu_requirements
end

function cooling_requirements:get_npu_requirements()
    if not self.npu_requirements then
        local npu_objs = self:get_objs_by_temp_type(f_enum.TemperatureType.NPUAiCore:value())
        self.npu_requirements = npu_objs
    end

    return self.npu_requirements
end

function cooling_requirements:obj_delete_callback(class_name, object, position)
    -- 对象卸载需要将相关的异常调速删除
    self.requirement_info_changed:emit('DeleteRequirementExp', object)

    if object[props.TEMPERATURE_TYPE] ~= f_enum.TemperatureType.InvalidType:value() then
        self:delete_from_type_obj_map(object)
    end

    if not self.objs[object[props.REQUIREMENT_ID]] then
        log:error("Delete CoolingRequirement(0x%x) but object is nil", object[props.REQUIREMENT_ID])
        return
    end
    self.requirement_id_map[object[props.REQUIREMENT_ID]] = nil
    self.objs[object[props.REQUIREMENT_ID]] = nil

    self:set_requirement_updated_flag(true)
end

local component_name_map = {
    [1] = 'CPU core',
    [2] = 'outlet',
    [3] = 'disk',
    [4] = 'memory',
    [5] = 'pch',
    [6] = 'vrd',
    [7] = 'vddq',
    [8] = 'hbm',
    [9] = 'aicore',
    [10] = 'npuboard',
}

-- 设置CoolingRequirement数据库数据
local function set_requirement_db_prop(self, primary_key_value, prop_name, prop_value)
    local json_prop_value = prop_value
    if self.db.CoolingRequirement:persistences(prop_name) == 'protect_power_off' then
        if type(prop_value) == 'table' then
            json_prop_value = cjson.encode(prop_value)
        end
        self.persist:per_save(sqlite3.UPDATE, 't_cooling_requirement', {{'RequirementId', primary_key_value}},
            {[prop_name] = {value = json_prop_value, persist_type = 'protect_power_off'}})
    end
end

local smart_cooling_target_temperature = {
    [enums.smart_cooling_mode.COOLING_ENERGY_SAVING_MODE] = 1,
    [enums.smart_cooling_mode.COOLING_HIGH_PERFORMANCE_MODE] = 2,
    [enums.smart_cooling_mode.COOLING_LOW_NOISE_MODE] = 3
}

-- 设置SmartCooling数组
function cooling_requirements:SetSmartCoolingTargetTemperature(obj, ctx, smart_cooling_temp_arr)
    local req_obj = self:get_requirement_by_id(obj[props.REQUIREMENT_ID])
    if #req_obj[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] ~= SMART_COOLING_TABLE_LENGTH then
        log:error('CoolingRequirement(id: %s) is not support smart cooling target temperature celsius',
            req_obj[props.REQUIREMENT_ID])
        error(custom_msg.PropertyModificationNotSupported('%SmartCoolingTargetTemperature'))
    end

    if #smart_cooling_temp_arr ~= SMART_COOLING_TABLE_LENGTH then
        log:error('The length of SmartCoolingTargetTemperature is %s, which is invalid', #smart_cooling_temp_arr)
        error(custom_msg.ArraySizeTooShort('%SmartCoolingTargetTemperature', SMART_COOLING_TABLE_LENGTH))
    end
    for idx = 1, SMART_COOLING_TABLE_LENGTH, 1 do
        if smart_cooling_temp_arr[idx] > req_obj[props.ORIGIN_SMART_COOLING_TARTGET_TEMPERATURE][idx] then
            log:error('The value [%s] of the property CoolingRequirement(id: %s) %s is invalid, ' ..
                'each imported value must be smaller than the original value[%s]',
                table.concat(smart_cooling_temp_arr, ','),
                req_obj[props.REQUIREMENT_ID],
                'SmartCoolingTargetTemperatureCelsius',
                table.concat(req_obj[props.ORIGIN_SMART_COOLING_TARTGET_TEMPERATURE], ',')
            )
            error(custom_msg.ValueOutOfRange('%SmartCoolingTargetTemperature'))
        end
    end
    if mc_utils.table_compare(req_obj[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS], smart_cooling_temp_arr) then
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.SMART_COOLING_TARTGET_TEMPERATURE,
            smart_cooling_temp_arr)
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS,
            smart_cooling_temp_arr)
    else
        req_obj[props.SMART_COOLING_TARTGET_TEMPERATURE] = smart_cooling_temp_arr
        req_obj[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] = smart_cooling_temp_arr
    end
    local smart_cooling_mode = self.fans_config_instance:get_smart_cooling_mode()
    if not smart_cooling_mode then
        return
    end
    local index = smart_cooling_target_temperature[smart_cooling_mode]
    if index then
        req_obj[props.TARGET_TEMP_CELSIUS] = smart_cooling_temp_arr[index]
    end
    self:set_requirement_updated_flag(true)
end

-- 设置目标温度
function cooling_requirements:SetBasicCoolingTargetTemperature(obj, ctx, target_temp)
    local req_obj = self:get_requirement_by_id(obj[props.REQUIREMENT_ID])
    if target_temp < 0 or target_temp > req_obj[props.ORIGIN_TARGET_TEMP_CELSIUS] then
        log:error('The value %s of the property CoolingRequirement(id: %s) %s is out of range, expect[0-%s]',
            target_temp, req_obj[props.REQUIREMENT_ID],
            'TargetTemperatureCelsius', 
            req_obj[props.ORIGIN_TARGET_TEMP_CELSIUS]
        )
        error(custom_msg.ValueOutOfRange('%TargetTemperature'))
    end
    if target_temp == req_obj[props.TARGET_TEMP_CELSIUS] then
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.TARGET_TEMP_CELSIUS, target_temp)
    else
        req_obj[props.TARGET_TEMP_CELSIUS] = target_temp
    end
    self:set_requirement_updated_flag(true)
end

function cooling_requirements:SetTargetTemperature(obj, ctx, temp_value)
    local temp_type, success = obj.TemperatureType, false
    local objs = self:get_objs_by_temp_type(temp_type)
    if not objs then
        utils.op(ctx, "Set target temperature of %s temperature adjustment policy(type:%s) failed",
            component_name_map[temp_type] and component_name_map[temp_type] or 'N/A', temp_type)
        error(custom_msg.PropertyModificationNotSupported('%TemperatureCelsius'))
    end
    local base_id
    for _, o in pairs(objs) do
        base_id = get_base_id(o[props.REQUIREMENT_ID])
        -- 若是无法匹配到对应的CoolingArea，那么无法设置自定义目标温度
        if not self.cooling_areas[base_id] then
            log:error('The CoolingArea(%s) is not exist', base_id)
            utils.op(ctx, "Set target temperature of %s temperature adjustment policy(id:0x%x) failed",
                component_name_map[temp_type] and component_name_map[temp_type] or 'N/A',
                o[props.REQUIREMENT_ID])
            goto continue
        end

        if not o.CustomSupported then
            log:error('Cooling requirement(id:0x%x) dose not support custom', obj[props.REQUIREMENT_ID])
            utils.op(ctx, "Set target temperature of %s temperature adjustment policy(id:0x%x) failed",
                component_name_map[temp_type] and component_name_map[temp_type] or 'N/A',
                o[props.REQUIREMENT_ID])
            goto continue
        end

        local tg_temp = o[props.TARGET_TEMP_RANGE_CELSIUS]
        local temp_range = type(tg_temp) == 'string' and {string.byte(tg_temp, 1, #tg_temp)} or tg_temp
        if not temp_range or #temp_range ~= 2 or temp_value < temp_range[1] or
            temp_value > temp_range[2] then
            log:error('Current val(%u) out of range[%u %u]', temp_value, temp_range[1], temp_range[2])
            utils.op(ctx, "Set target temperature of %s temperature adjustment policy(id:0x%x) failed",
                component_name_map[temp_type] and component_name_map[temp_type] or 'N/A',
                o[props.REQUIREMENT_ID])
            goto continue
        end
        o.CustomTargetTemperatureCelsius = temp_value
        utils.op(ctx, "Set target temperature to %d of %s temperature adjustment policy(id:0x%x) successfully",
            temp_value, component_name_map[temp_type] and component_name_map[temp_type] or 'N/A',
            o[props.REQUIREMENT_ID])
        log:info('Set the target temp of requirement objs(id:0x%x, type:%u) to %u', o[props.REQUIREMENT_ID],
            temp_type, temp_value)
        success = true
        ::continue::
    end
    if not success then
        utils.op(ctx, "Set target temperature of %s temperature adjustment policy(type:%s) failed",
            component_name_map[temp_type] and component_name_map[temp_type] or 'N/A', temp_type)
        error(custom_msg.PropertyModificationNotSupported('%TemperatureCelsius'))
    end
end

-- 设置全速阈值
function cooling_requirements:SetMaxAllowedTemperature(obj, ctx, max_allowed_temp)
    local req_obj = self:get_requirement_by_id(obj[props.REQUIREMENT_ID])
    if max_allowed_temp < 0 or max_allowed_temp > req_obj[props.ORIGIN_MAX_ALLOWED_TEMPERATURE_CELSIUS] then
        log:error('The value %s of the property CoolingRequirement(id: %s) %s is out of range, expect[0-%s]',
            max_allowed_temp, obj[props.REQUIREMENT_ID], max_allowed_temp,
            req_obj[props.ORIGIN_MAX_ALLOWED_TEMPERATURE_CELSIUS]
        )
        error(custom_msg.ValueOutOfRange('%MaxAllowedTemperature'))
    end
    if max_allowed_temp == req_obj[props.MAX_ALLOWED_TEMPERATURE_CELSIUS] then
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.MAX_ALLOWED_TEMPERATURE_CELSIUS,
            max_allowed_temp)
    else
        req_obj[props.MAX_ALLOWED_TEMPERATURE_CELSIUS] = max_allowed_temp
    end
    self:set_requirement_updated_flag(true)
end

-- 温度失效转速
function cooling_requirements:SetFailedValue(obj, ctx, failed_value)
    local req_obj = self:get_requirement_by_id(obj[props.REQUIREMENT_ID])
    if failed_value > 100 or failed_value < req_obj[props.ORIGIN_FAILED_VALUE] then
        log:error('The value %s of the property CoolingRequirement(id: %s) %s is out of range, expect[%s-100]',
            failed_value, req_obj[props.REQUIREMENT_ID], 'FailedValue', req_obj[props.ORIGIN_FAILED_VALUE]
        )
        error(custom_msg.ValueOutOfRange('%FailedValue'))
    end
    if failed_value == obj[props.FAILED_VALUE] then
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.FAILED_VALUE, failed_value)
    else
        req_obj[props.FAILED_VALUE] = failed_value
    end
    self:set_requirement_updated_flag(true)
end

-- 通过temperaturetype获取到对应的requirement对象，同一类型requirement可能有多个，返回一个table
function cooling_requirements:get_objs_by_temp_type(temp_type)
    return self.type_obj_map[temp_type]
end

-- 获取第一个temperaturetype为特定值的对象
function cooling_requirements:get_first_obj_by_temp_type(temp_type)
    local objs = self.type_obj_map[temp_type]
    if not objs then
        return nil
    end
    for _, v in pairs(objs) do
        return v
    end
end

function cooling_requirements:insert_to_type_obj_map(obj)
    if not self.type_obj_map[obj.TemperatureType] then
        self.type_obj_map[obj.TemperatureType] = {}
    end
    self.type_obj_map[obj.TemperatureType][obj[props.REQUIREMENT_ID]] = obj
end

function cooling_requirements:delete_from_type_obj_map(obj)
    local objs = self.type_obj_map[obj.TemperatureType]
    if not objs then
        log:error("Delete obj from type_obj_map failed, type:%u, id:0x%x", obj.TemperatureType,
            obj[props.REQUIREMENT_ID])
        return
    end

    objs[obj[props.REQUIREMENT_ID]] = nil
end

function cooling_requirements:get_requirement_by_id(requirement_id)
    return self.objs[requirement_id]
end

-- 获取主用温度点
function cooling_requirements:get_requirement_by_backup_idx(backup_requirement)
    for _, obj in pairs(self.objs) do
        if obj.BackupRequirementIdx and obj.BackupRequirementIdx == backup_requirement[props.REQUIREMENT_ID] then
            return obj
        end
    end
end

function cooling_requirements:get_objs()
    return self.objs
end

function cooling_requirements:delete_objs(requirement_ids)
    if type(requirement_ids) == 'table' then
        for _, requirement_id in pairs(requirement_ids) do
            if self.objs[requirement_id] then
                self.objs[requirement_id] = nil
            end
        end
    elseif type(requirement_ids) == 'number' then
        if self.objs[requirement_ids] then
            self.objs[requirement_ids] = nil
        end
    else
        log:debug('RequirementId is invalid type')
    end
end


function cooling_requirements:is_monitoring_abnormal(requirement_o)
    if not requirement_o.BackupRequirementIdx or requirement_o.BackupRequirementIdx <= 0 then
        -- 无备用温度点
        return requirement_o.MonitoringStatus ~= 0
    end

    if requirement_o.MonitoringStatus ~= 0 then
        -- 有备用温度点
        local backup_requirement_o = self:get_requirement_by_id(requirement_o.BackupRequirementIdx)
        if not backup_requirement_o then
            log:debug('Backup requirement does not find, check whether backup requirement id(%s) exists',
                requirement_o.BackupRequirementIdx)
            return true
        end
        -- 主用温度点状态异常，则判断备用温度点状态
        return backup_requirement_o.MonitoringStatus ~= 0
    end

    return false
end

function cooling_requirements:get_temperature_value(requirement_o)
    -- 无备用温度点
    if not requirement_o.BackupRequirementIdx or requirement_o.BackupRequirementIdx <= 0 then
        return requirement_o.MonitoringValue
    end
    -- 主温度点异常，则使用备用温度点调速
    if requirement_o.MonitoringStatus ~= 0 then
        -- 有备用温度点
        local backup_requirement_o = self:get_requirement_by_id(requirement_o.BackupRequirementIdx)
        return backup_requirement_o and backup_requirement_o.MonitoringValue or 255
    else
        return requirement_o.MonitoringValue
    end
end

-- requirement生效判断，返回不生效返回原因
function cooling_requirements:get_requirement_invalid_cause(disk_temp_available, tmp_requirement, power_state)
    -- 如果用户配置的自定义生效条件为false则表示不生效
    if not tmp_requirement.Enabled then
        return 'Disabled'
    end

    -- 作为备用温度点，且未配置目标调速值则无效
    if tmp_requirement.IsBackupRequirement and tmp_requirement.TargetTemperatureCelsius == 0 then
        return 'IsBackupRequirement'
    end

    -- 某些温度值获取不到才生效
    if tmp_requirement.ObtainTempFaildToValid and not disk_temp_available then
        return 'ObtainTempSuccess'
    end
    if (power_state == enums.power_status.OFF or power_state == enums.power_status.ONING) and
        not tmp_requirement[props.ACTIVE_IN_STANDBY] then
        return 'PowerNotMeet'
    end

    return 'None'
end

-- 更新目标调速和曲线调速的生效状态
function cooling_requirements:update_objs_isvalid(data, disk_temp_available, power_state, fans_config_o)
    for id, obj in pairs(self.objs) do
        local invalid_cause = self:get_requirement_invalid_cause(disk_temp_available, obj, power_state)
        if invalid_cause == 'None' then
            obj.IsValid = 1
        else
            obj.IsValid = 0
        end
        -- 更新曲线调速的状态
        self.policys_instance:update_policy_isvalid(data, obj, fans_config_o)
        log:debug("Update requirement(id:0x%x) IsValid to %u, Invalid cause: %s", id, obj.IsValid, invalid_cause)
    end
end

-- 私有属性变更回调
function cooling_requirements:private_property_changed_callback(object, prop, value)
    if prop == 'IsValid' then
        -- 生效状态变更，重新下发目标调速配置到pid
        self:set_requirement_updated_flag(true)
    end
end

function cooling_requirements.is_target_temp_cooling(object)
    if not object.MaxAllowedTemperatureCelsius or not object.TargetTemperatureCelsius then
        return false
    end
    if object.MaxAllowedTemperatureCelsius == 0 or object.TargetTemperatureCelsius == 0 then
        return false
    end

    return true
end

function cooling_requirements:get_sensor_name_by_id(requirement_id, exp_type)
    local obj = self:get_requirement_by_id(requirement_id)
    if not obj then
        log:error('Requirement(%s) does not exist', requirement_id)
        return nil
    end
    if exp_type == cooling_enums.max_fan_pwm_type.REQUIREMENT then
        return obj.SensorName
    end
    if obj.BackupRequirementIdx ~= 0 then
        -- 存在备用温度点索引
        local back_obj = self:get_requirement_by_id(obj.BackupRequirementIdx)
        -- 若未获取到备用温度点对象说明配置错误或者备用温度点对象未分发
        if not back_obj then
            log:error('BackupRequirementIdx(%s) does not exist', requirement_id)
            return nil
        end
        return obj.MonitoringStatus == 0 and obj.SensorName or back_obj.SensorName
    end
    return obj.SensorName
end

-- 通过CoolingAreaId获取对应的风扇Id
function cooling_requirements:get_fan_idx_group_by_areaId(areaId)
    if not self.cooling_areas then
        log:debug('The Cooling area is null')
        return {}
    end
    if not self.cooling_areas[areaId] then
        log:debug('The CoolingArea(%s) is not exist', areaId)
        return {}
    end
    return self.cooling_areas[areaId][props.FAN_IDX_GROUP]
end

function cooling_requirements:get_pump_idx_group()
    if not self.cooling_areas then
        log:debug('The cooling area is null')
        return {}
    end
    for _, obj in pairs(self.cooling_areas) do
        if string.find(obj[props.OBJECT_NAME], props.PUMP_AREA_NAME) and obj[props.LIQUID_DEVICE_GROUP] then
            return obj[props.LIQUID_DEVICE_GROUP]
        end
    end
    return {}
end

-- 初始化获取自定义目标温度点返回体
local function init_custom_target_temperatures(data)
    for type_id = 1, 10, 1 do
        data[type_id].TemperatureType = type_id
        data[type_id].TargetTemperatureCelsius = 0
        data[type_id].MinTargetTemperatureCelsius = 0
        data[type_id].MaxTargetTemperatureCelsius = 0
    end
end

-- 获取自定义目标温度点
function cooling_requirements:get_custom_target_temperatures()
    -- 初始化为10个空数组，防止接口直接nil.属性访问异常
    local rsp = {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}}
    init_custom_target_temperatures(rsp)
    -- 温度点类型 1:Cpu 2:Outlet 3:Disk 4:Memory 5:PCH 6:VRD 7:VDDQ 8:NPUHbm 9:NPUAiCore 10:NPUBoard
    for type_id = 1, 10, 1 do
        if not self.type_obj_map[type_id] then
            goto continue
        end
        -- 同一类型Type的requirement可能有多个
        for _, obj in pairs(self.type_obj_map[type_id]) do
            local base_id = get_base_id(obj[props.REQUIREMENT_ID])
            -- 若是无法匹配到对应的CoolingArea，那么认为是不支持温度点自定义(共板场景)
            if not self.cooling_areas[base_id] then
                goto next
            end

            -- 不支持设置的不返回
            if obj[props.POLICY_CUSTOM_SUPPORTED] then
                rsp[type_id].TemperatureType = type_id
                rsp[type_id].TargetTemperatureCelsius = obj.CustomTargetTemperatureCelsius
                -- 配置了支持设置但是漏配置上下限范围返回0而不是导致接口报错
                rsp[type_id].MinTargetTemperatureCelsius = obj.TargetTemperatureRangeCelsius[1] or 0
                rsp[type_id].MaxTargetTemperatureCelsius = obj.TargetTemperatureRangeCelsius[2] or 0
                break
            end
            ::next::
        end
        ::continue::
    end
    return rsp
end

-- 添加目标调速值
function cooling_requirements:get_custom_target_temp()
    local table = {}
    local custom_temperature_type = {
        [f_enum.TemperatureType.Outlet:value()] = 'Custom_OutletObjTem',
        [f_enum.TemperatureType.Cpu:value()] = 'Custom_CoreRemObjTem'
    }
    for type, obj_name in pairs(custom_temperature_type) do
        local find_obj = self:get_first_obj_by_temp_type(type)
        if not find_obj then
            log:error('Get object(%s) failed', obj_name)
            goto continue
        end
        local custom_supported = find_obj[props.POLICY_CUSTOM_SUPPORTED]
        if not custom_supported then
            log:error('Object(%s) cannot be customized', obj_name)
            goto continue
        end
        local base_id = get_base_id(find_obj[props.REQUIREMENT_ID])
        -- 若是无法匹配到对应的CoolingArea，那么认为是温度点无效(共板场景)
        if not self.cooling_areas[base_id] then
            goto continue
        end
        table[obj_name] = find_obj[props.CUSTOM_TARGET_TEMP_CELSIUS]
        ::continue::
    end
    return table
end

local function getSpeed(temp, temp_low, temp_high, speed_low, speed_high)
    -- 遍历温度范围，找到对应的属性
    for i = 1, #temp_low, 1 do
        -- 根据调速策略配置规则，左闭右开
        if temp >= temp_low[i] and temp < temp_high[i] then
            -- 计算转速
            local Ratio = (speed_high[i] - speed_low[i]) / (temp_high[i] - temp_low[i])
            local speed = Ratio * (temp - temp_low[i]) + speed_low[i]
            return speed
        end
        -- 存在某个温度下计算不出转速直接返回0
        if temp < temp_low[i] then
            return 0
        end
    end
end

function cooling_requirements:GetSpecificPWM(Medium, PolicyType, SmartCoolingMode, Temperature)
    local control_pwm = 0
    local temp_pwm = 0
    local policies = self.policys_instance:get_objs()

    for _, requirement_obj in pairs(self.objs) do
        if TEMPERATURE_TYPE_MAP[requirement_obj[props.TEMPERATURE_TYPE]] ~= POLICY_TYPE_MAP[PolicyType] then
            goto continue
        end
        -- 温度点不生效
        if requirement_obj[props.IS_VALID] == 0 then
            goto continue
        end
        local req_base_id = requirement_obj[props.REQ_BASE_ID]
        local area_id = self.cooling_data.requirement_area_map[req_base_id]
        -- 通过Requirement的baseid无法匹配到Area,即Area对象中无requirementidx为baseid的
        if not area_id then
            log:debug('The CoolingRequirement(0x%x) can not match CoolingArea(RequirementIdx:%s)',
                requirement_obj[props.REQUIREMENT_ID], req_base_id)
            goto continue
        end
        local area_obj = self.cooling_data.cooling_areas[area_id]
        if not area_obj then
            goto continue
        end
        for _, policy_id in pairs(area_obj[props.POLICY_IDX_GROUP]) do
            local policy_obj = policies[policy_id]
            if policy_obj and self.policys_instance:get_policy_isvalid(policy_obj, Medium, SmartCoolingMode) then
                temp_pwm = getSpeed(Temperature, policy_obj.TemperatureRangeLow, policy_obj.TemperatureRangeHigh,
                    policy_obj.SpeedRangeLow, policy_obj.SpeedRangeHigh
                )
                control_pwm = temp_pwm > control_pwm and temp_pwm or control_pwm
            end
        end

        ::continue::
    end
    -- 如果返回为0表示当前输入参数无法计算出转速
    return control_pwm
end

return Singleton(cooling_requirements)